package com.ruogu.codegeneration.controller;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.ruogu.codegeneration.annotation.AuthCheck;
import com.ruogu.codegeneration.common.BaseResponse;
import com.ruogu.codegeneration.common.DeleteRequest;
import com.ruogu.codegeneration.common.ResultUtils;
import com.ruogu.codegeneration.constant.AppConstant;
import com.ruogu.codegeneration.constant.UserConstant;
import com.ruogu.codegeneration.exception.BusinessException;
import com.ruogu.codegeneration.exception.ErrorCode;
import com.ruogu.codegeneration.exception.ThrowUtils;
import com.ruogu.codegeneration.model.dto.app.*;
import com.ruogu.codegeneration.model.entity.App;
import com.ruogu.codegeneration.model.entity.User;
import com.ruogu.codegeneration.model.enums.CodeGenTypeEnum;
import com.ruogu.codegeneration.model.vo.app.AppVO;
import com.ruogu.codegeneration.service.AppService;
import com.ruogu.codegeneration.service.ProjectDownloadService;
import com.ruogu.codegeneration.service.UserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerSentEvent;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.io.File;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;

/**
 * 应用控制层
 *
 * @author ruogu
 */
@RestController
@RequestMapping("/app")
@Tag(name = "应用相关接口-AppController")
public class AppController {

    @Resource
    private AppService appService;

    @Resource
    private UserService userService;

    @Resource
    private ProjectDownloadService projectDownloadService;

    // ------------------------------- 用户相关接口 -------------------------

    /**
     * 应用部署
     *
     * @param appDeployRequest 部署请求
     * @param request          请求
     * @return 部署 URL
     */
    @PostMapping("/deploy")
    public BaseResponse<String> deployApp(@RequestBody AppDeployRequest appDeployRequest, HttpServletRequest request) {
        ThrowUtils.throwIf(appDeployRequest == null, ErrorCode.PARAMS_ERROR);
        Long appId = appDeployRequest.getAppId();
        ThrowUtils.throwIf(appId == null || appId <= 0, ErrorCode.PARAMS_ERROR, "应用 ID 不能为空");
        // 获取当前登录用户
        User loginUser = userService.getLoginUser(request);
        // 调用服务部署应用
        String deployUrl = appService.deployApp(appId, loginUser);
        return ResultUtils.success(deployUrl);
    }




    /**
     * 创建应用
     */
    @PostMapping("/add")
    @Operation(summary = "创建应用", description = "用户创建新应用，须填写 initPrompt")
    public BaseResponse<Long> addApp(@RequestBody AppAddRequest appAddRequest, HttpServletRequest request) {
        ThrowUtils.throwIf(appAddRequest == null, ErrorCode.PARAMS_ERROR);
        String initPrompt = appAddRequest.getInitPrompt();
        ThrowUtils.throwIf(initPrompt == null,ErrorCode.PARAMS_ERROR ,"初始化prompt不可为空");

        // 获取当前登录用户
        User loginUser = userService.getLoginUser(request);
        
        App app = new App();
        BeanUtil.copyProperties(appAddRequest, app);
        app.setUserId(loginUser.getId());
        // 应用名称使用prompt的前12位
        app.setAppName(initPrompt.substring(0, Math.min(12, initPrompt.length())));
        // 暂时写成多文件
        app.setCodeGenType(CodeGenTypeEnum.VUE_PROJECT.getValue());
        // 校验参数
        appService.validApp(app, true);
        
        boolean result = appService.save(app);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
        return ResultUtils.success(app.getId());
    }

    /**
     * 根据 id 修改自己的应用（目前只支持修改应用名称）
     */
    @PostMapping("/my/update")
    @Operation(summary = "修改自己的应用", description = "用户修改自己的应用，目前只支持修改应用名称")
    public BaseResponse<Boolean> updateMyApp(@RequestBody AppMyUpdateRequest appMyUpdateRequest, HttpServletRequest request) {
        ThrowUtils.throwIf(appMyUpdateRequest == null || appMyUpdateRequest.getId() == null, ErrorCode.PARAMS_ERROR);
        
        // 获取原应用
        App oldApp = appService.getById(appMyUpdateRequest.getId());
        ThrowUtils.throwIf(oldApp == null, ErrorCode.NOT_FOUND_ERROR);
        
        // 检查权限
        ThrowUtils.throwIf(appService.checkAppPermission(oldApp, request), ErrorCode.NO_AUTH_ERROR);
        
        App app = new App();
        BeanUtil.copyProperties(appMyUpdateRequest, app);
        app.setEditTime(LocalDateTime.now());
        
        // 校验参数
        appService.validApp(app, false);
        
        boolean result = appService.updateById(app);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
        return ResultUtils.success(true);
    }

    /**
     * 根据 id 删除自己的应用
     */
    @PostMapping("/my/delete")
    @Operation(summary = "删除自己的应用", description = "用户删除自己的应用")
    public BaseResponse<Boolean> deleteMyApp(@RequestBody DeleteRequest deleteRequest, HttpServletRequest request) {
        ThrowUtils.throwIf(deleteRequest == null || deleteRequest.getId() == null || deleteRequest.getId() <= 0,
                ErrorCode.PARAMS_ERROR);
        
        // 获取原应用
        App oldApp = appService.getById(deleteRequest.getId());
        ThrowUtils.throwIf(oldApp == null, ErrorCode.NOT_FOUND_ERROR);
        
        // 检查权限
        ThrowUtils.throwIf(appService.checkAppPermission(oldApp, request), ErrorCode.NO_AUTH_ERROR);
        
        boolean result = appService.removeById(deleteRequest.getId());
        return ResultUtils.success(result);
    }

    /**
     * 根据 id 查看应用详情
     */
    @GetMapping("/get/vo")
    @Operation(summary = "根据ID获取应用详情", description = "根据应用ID获取应用详细信息")
    public BaseResponse<AppVO> getAppVoById(long id, HttpServletRequest request) {
        ThrowUtils.throwIf(id <= 0, ErrorCode.PARAMS_ERROR);
        App app = appService.getById(id);
        ThrowUtils.throwIf(app == null, ErrorCode.NOT_FOUND_ERROR);
        AppVO appVO = appService.getAppVO(app);
        Long userId = appVO.getUserId();
        if(userId != null){
            User user = userService.getById(userId);
            appVO.setUser(userService.getUserVO(user));
        }
        return ResultUtils.success(appVO);
    }

    /**
     * 分页查询自己的应用列表（支持根据名称查询，每页最多 20 个）
     */
    @PostMapping("/my/list/page/vo")
    @Operation(summary = "分页查询自己的应用列表", description = "用户分页查询自己的应用列表，支持根据名称查询")
    public BaseResponse<Page<AppVO>> listMyAppVoByPage(@RequestBody AppQueryRequest appQueryRequest, HttpServletRequest request) {
        ThrowUtils.throwIf(appQueryRequest == null, ErrorCode.PARAMS_ERROR);
        
        // 获取当前登录用户
        User loginUser = userService.getLoginUser(request);
        
        // 限制查询自己的应用
        appQueryRequest.setUserId(loginUser.getId());
        long pageNum = appQueryRequest.getPageNum();
        // 限制最多20个
        long pageSize = Math.min(appQueryRequest.getPageSize(), 20);
        Page<App> appPage = appService.page(Page.of(pageNum, pageSize),
                appService.getQueryWrapper(appQueryRequest));
        
        Page<AppVO> appVoPage = new Page<>(pageNum, pageSize, appPage.getTotalRow());
        List<AppVO> appVOList = appService.getAppVOList(appPage.getRecords());
        appVoPage.setRecords(appVOList);
        return ResultUtils.success(appVoPage);
    }

    /**
     * 分页查询精选的应用列表（支持根据名称查询，每页最多 20 个）
     */
    @PostMapping("/list/page/vo")
    @Operation(summary = "分页查询精选应用列表", description = "分页查询精选的应用列表，支持根据名称查询")
    public BaseResponse<Page<AppVO>> listAppVoByPage(@RequestBody AppQueryRequest appQueryRequest) {
        ThrowUtils.throwIf(appQueryRequest == null, ErrorCode.PARAMS_ERROR);
        
        long pageNum = appQueryRequest.getPageNum();
        // 限制最多20个
        long pageSize = Math.min(appQueryRequest.getPageSize(), 20);
        appQueryRequest.setPriority(AppConstant.GOOD_APP_PRIORITY);
        // 构建查询条件
        QueryWrapper queryWrapper = appService.getQueryWrapper(appQueryRequest);
        Page<App> appPage = appService.page(Page.of(pageNum, pageSize), queryWrapper);
        
        Page<AppVO> appVoPage = new Page<>(pageNum, pageSize, appPage.getTotalRow());
        List<AppVO> appVOList = appService.getAppVOList(appPage.getRecords());
        appVoPage.setRecords(appVOList);
        return ResultUtils.success(appVoPage);
    }

    // ------------------------------- 管理员相关接口 -------------------------

    /**
     * 根据 id 删除任意应用（管理员）
     */
    @PostMapping("/delete")
    @AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
    @Operation(summary = "删除应用", description = "管理员根据应用ID删除任意应用")
    public BaseResponse<Boolean> deleteApp(@RequestBody DeleteRequest deleteRequest) {
        ThrowUtils.throwIf(deleteRequest == null || deleteRequest.getId() == null || deleteRequest.getId() <= 0,
                ErrorCode.PARAMS_ERROR);
        boolean b = appService.removeById(deleteRequest.getId());
        return ResultUtils.success(b);
    }

    /**
     * 根据 id 更新任意应用（管理员）
     */
    @PostMapping("/update")
    @AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
    @Operation(summary = "更新应用", description = "管理员更新应用信息，支持更新应用名称、应用封面、优先级")
    public BaseResponse<Boolean> updateApp(@RequestBody AppUpdateRequest appUpdateRequest) {
        ThrowUtils.throwIf(appUpdateRequest == null || appUpdateRequest.getId() == null,
                ErrorCode.PARAMS_ERROR);
        App app = new App();
        BeanUtil.copyProperties(appUpdateRequest, app);
        app.setEditTime(LocalDateTime.now());
        // 校验参数
        appService.validApp(app, false);
        
        boolean result = appService.updateById(app);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
        return ResultUtils.success(true);
    }

    /**
     * 分页查询应用列表（管理员）
     */
    @PostMapping("/list/page/vo/admin")
    @AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
    @Operation(summary = "分页获取应用列表", description = "管理员分页获取应用列表，支持根据除时间外的任何字段查询")
    public BaseResponse<Page<AppVO>> listAppVoByPageAdmin(@RequestBody AppQueryRequest appQueryRequest) {
        ThrowUtils.throwIf(appQueryRequest == null, ErrorCode.PARAMS_ERROR);
        long pageNum = appQueryRequest.getPageNum();
        long pageSize = appQueryRequest.getPageSize();
        Page<App> appPage = appService.page(Page.of(pageNum, pageSize),
                appService.getQueryWrapper(appQueryRequest));
        
        Page<AppVO> appVoPage = new Page<>(pageNum, pageSize, appPage.getTotalRow());
        List<AppVO> appVOList = appService.getAppVOList(appPage.getRecords());
        appVoPage.setRecords(appVOList);
        return ResultUtils.success(appVoPage);
    }

    /**
     * 根据 id 获取应用（管理员）
     */
    @GetMapping("/get")
    @AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
    @Operation(summary = "根据ID获取应用", description = "管理员根据应用ID获取应用详细信息")
    public BaseResponse<App> getAppById(long id) {
        ThrowUtils.throwIf(id <= 0, ErrorCode.PARAMS_ERROR);
        App app = appService.getById(id);
        ThrowUtils.throwIf(app == null, ErrorCode.NOT_FOUND_ERROR);
        return ResultUtils.success(app);
    }

    /**
     * 应用聊天生成代码（流式 SSE）
     *
     * @param appId   应用 ID
     * @param message 用户消息
     * @param request 请求对象
     * @return 生成结果流
     */
    @GetMapping(value = "/chat/gen/code", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<ServerSentEvent<String>> chatToGenCode(@RequestParam Long appId,
                                      @RequestParam String message,
                                      HttpServletRequest request) {
        // 参数校验
        ThrowUtils.throwIf(appId == null || appId <= 0, ErrorCode.PARAMS_ERROR, "应用ID无效");
        ThrowUtils.throwIf(StrUtil.isBlank(message), ErrorCode.PARAMS_ERROR, "用户消息不能为空");
        // 获取当前登录用户
        User loginUser = userService.getLoginUser(request);
        // 调用服务生成代码（流式）
        Flux<String> contentFlux = appService.chatToGenCode(appId, message, loginUser);
        return contentFlux.map(content -> {
            // 将内容包装一层返回
            Map<String, String> map = Map.of("v", content);
            String str = JSONUtil.toJsonStr(map);
            return ServerSentEvent.<String>builder().data(str).build();
        }).concatWith(
                Mono.just(
                        ServerSentEvent.<String>builder()
                                .event("done")
                                .data("")
                                .build()
                )
        );
    }


    /**
     * 下载应用代码
     *
     * @param appId    应用ID
     * @param request  请求
     * @param response 响应
     */
    @GetMapping("/download/{appId}")
    public void downloadAppCode(@PathVariable Long appId,
                                HttpServletRequest request,
                                HttpServletResponse response) {
        // 1. 基础校验
        ThrowUtils.throwIf(appId == null || appId <= 0, ErrorCode.PARAMS_ERROR, "应用ID无效");
        // 2. 查询应用信息
        App app = appService.getById(appId);
        ThrowUtils.throwIf(app == null, ErrorCode.NOT_FOUND_ERROR, "应用不存在");
        // 3. 权限校验：只有应用创建者可以下载代码
        User loginUser = userService.getLoginUser(request);
        if (!app.getUserId().equals(loginUser.getId())) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "无权限下载该应用代码");
        }
        // 4. 构建应用代码目录路径（生成目录，非部署目录）
        String codeGenType = app.getCodeGenType();
        String sourceDirName = codeGenType + "_" + appId;
        String sourceDirPath = AppConstant.CODE_OUTPUT_ROOT_DIR + File.separator + sourceDirName;
        // 5. 检查代码目录是否存在
        File sourceDir = new File(sourceDirPath);
        ThrowUtils.throwIf(!sourceDir.exists() || !sourceDir.isDirectory(),
                ErrorCode.NOT_FOUND_ERROR, "应用代码不存在，请先生成代码");
        // 6. 生成下载文件名（不建议添加中文内容）
        String downloadFileName = String.valueOf(appId);
        // 7. 调用通用下载服务
        projectDownloadService.downloadProjectAsZip(sourceDirPath, downloadFileName, response);
    }



}